home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / sbin / update-rc.d-insserv < prev    next >
Text File  |  2009-09-07  |  17KB  |  577 lines

  1. #! /usr/bin/perl
  2. #
  3. # update-rc.d    Update the links in /etc/rc[0-9S].d/
  4. #
  5.  
  6. use strict;
  7. use warnings;
  8.  
  9. my $initd = "/etc/init.d";
  10. my $etcd  = "/etc/rc";
  11. my $notreally = 0;
  12.  
  13. # Print usage message and die.
  14.  
  15. sub usage {
  16.     print STDERR "update-rc.d: error: @_\n" if ($#_ >= 0);
  17.     print STDERR <<EOF;
  18. usage: update-rc.d [-n] [-f] <basename> remove
  19.        update-rc.d [-n] <basename> defaults [NN | SS KK]
  20.        update-rc.d [-n] <basename> start|stop NN runlvl [runlvl] [...] .
  21.        update-rc.d [-n] <basename> disable|enable [S|2|3|4|5]
  22.         -n: not really
  23.         -f: force
  24.  
  25. The disable|enable API is not stable and might change in the future.
  26. EOF
  27.     exit (1);
  28. }
  29.  
  30. # Dependency based boot sequencing is the default, but upgraded
  31. # systems might keep the legacy ordering until the sysadm choose to
  32. # migrate to the new ordering method.  sysv-rc version 2.87dsf-2 will
  33. # remove /var/lib/insserv/using-insserv and this divert, thus transfering
  34. # the responsibility for dependency based update-rc.d to sysv-rc.
  35. if ( -f "/var/lib/insserv/using-insserv" && ! -f "/etc/init.d/.legacy-bootordering" ) {
  36.     info("using dependency based boot sequencing");
  37.     exit insserv_updatercd(@ARGV);
  38. }
  39.  
  40. # Check out options.
  41. my $force;
  42.  
  43. my @orig_argv = @ARGV;
  44.  
  45. while($#ARGV >= 0 && ($_ = $ARGV[0]) =~ /^-/) {
  46.     shift @ARGV;
  47.     if (/^-n$/) { $notreally++; next }
  48.     if (/^-f$/) { $force++; next }
  49.     if (/^-h|--help$/) { &usage; }
  50.     &usage("unknown option");
  51. }
  52.  
  53. sub save_last_action {
  54.     my ($script, @arguments) = @_;
  55.     my $archive = "/var/lib/update-rc.d";
  56.  
  57.     return if $notreally;
  58.  
  59.     open(FILE, ">", "$archive/${script}.new") || die;
  60.     print FILE join(" ","update-rc.d",@arguments), "\n";
  61.     close(FILE);
  62.     rename "$archive/${script}.new", "$archive/${script}";
  63. }
  64.  
  65. # Action.
  66.  
  67. &usage() if ($#ARGV < 1);
  68. my $bn = shift @ARGV;
  69.  
  70. unless ($bn =~ m/[a-zA-Z0-9+.-]+/) {
  71.     print STDERR "update-rc.d: illegal character in name '$bn'\n";
  72.     exit (1);
  73. }
  74.  
  75. if ($ARGV[0] ne 'remove') {
  76.     if (! -f "$initd/$bn") {
  77.     print STDERR "update-rc.d: $initd/$bn: file does not exist\n";
  78.     exit (1);
  79.     }
  80.     &parse_lsb_header("$initd/$bn");
  81.     &cmp_args_with_defaults($bn, $ARGV[0], @ARGV);
  82. } elsif (-f "$initd/$bn") {
  83.     if (!$force) {
  84.     printf STDERR "update-rc.d: $initd/$bn exists during rc.d purge (use -f to force)\n";
  85.     exit (1);
  86.     }
  87. }
  88.  
  89. my @startlinks;
  90. my @stoplinks;
  91.  
  92. $_ = $ARGV[0];
  93. if    (/^remove$/)       { &checklinks ("remove"); save_last_action($bn, @orig_argv); }
  94. elsif (/^defaults$/)     { &defaults (@ARGV); &makelinks; save_last_action($bn, @orig_argv); }
  95. elsif (/^(start|stop)$/) { &startstop (@ARGV); &makelinks; save_last_action($bn, @orig_argv); }
  96. elsif (/^(dis|en)able$/) { &toggle (@ARGV); &makelinks; save_last_action($bn, @orig_argv); }
  97. else                     { &usage; }
  98.  
  99. exit (0);
  100.  
  101. sub info {
  102.     print STDOUT "update-rc.d: @_\n";
  103. }
  104.  
  105. sub warning {
  106.     print STDERR "update-rc.d: warning: @_\n";
  107. }
  108.  
  109. sub error {
  110.     print STDERR "update-rc.d: error: @_\n";
  111.     exit (1);
  112. }
  113.  
  114. # Check if there are links in /etc/rc[0-9S].d/ 
  115. # Remove if the first argument is "remove" and the links 
  116. # point to $bn.
  117.  
  118. sub is_link () {
  119.     my ($op, $fn, $bn) = @_;
  120.     if (! -l $fn) {
  121.     warning "$fn is not a symbolic link\n";
  122.     return 0;
  123.     } else {
  124.     my $linkdst = readlink ($fn);
  125.     if (! defined $linkdst) {
  126.         die ("update-rc.d: error reading symbolic link: $!\n");
  127.     }
  128.     if (($linkdst ne "../init.d/$bn") && ($linkdst ne "$initd/$bn")) {
  129.         warning "$fn is not a link to ../init.d/$bn or $initd/$bn\n";
  130.         return 0;
  131.     }
  132.     }
  133.     return 1;
  134. }
  135.  
  136. sub checklinks {
  137.     my ($i, $found, $fn, $islnk);
  138.  
  139.     print " Removing any system startup links for $initd/$bn ...\n"
  140.     if (defined $_[0] && $_[0] eq 'remove');
  141.  
  142.     $found = 0;
  143.  
  144.     foreach $i (0..9, 'S') {
  145.     unless (chdir ("$etcd$i.d")) {
  146.         next if ($i =~ m/^[789S]$/);
  147.         die("update-rc.d: chdir $etcd$i.d: $!\n");
  148.     }
  149.     opendir(DIR, ".");
  150.     my $saveBN=$bn;
  151.     $saveBN =~ s/\+/\\+/g;
  152.     foreach $_ (readdir(DIR)) {
  153.         next unless (/^[SK]\d\d$saveBN$/);
  154.         $fn = "$etcd$i.d/$_";
  155.         $found = 1;
  156.         $islnk = &is_link ($_[0], $fn, $bn);
  157.         next unless (defined $_[0] and $_[0] eq 'remove');
  158.         if (! $islnk) {
  159.         print "   $fn is not a link to ../init.d/$bn; not removing\n"; 
  160.         next;
  161.         }
  162.         print "   $etcd$i.d/$_\n";
  163.         next if ($notreally);
  164.         unlink ("$etcd$i.d/$_") ||
  165.         die("update-rc.d: unlink: $!\n");
  166.     }
  167.     closedir(DIR);
  168.     }
  169.     $found;
  170. }
  171.  
  172. sub parse_lsb_header {
  173.     my $initdscript = shift;
  174.     my %lsbinfo;
  175.     my $lsbheaders = "Provides|Required-Start|Required-Stop|Default-Start|Default-Stop";
  176.     open(INIT, "<$initdscript") || die "error: unable to read $initdscript";
  177.     while (<INIT>) {
  178.         chomp;
  179.         $lsbinfo{'found'} = 1 if (m/^\#\#\# BEGIN INIT INFO\s*$/);
  180.         last if (m/\#\#\# END INIT INFO\s*$/);
  181.         if (m/^\# ($lsbheaders):\s*(\S?.*)$/i) {
  182.         $lsbinfo{lc($1)} = $2;
  183.         }
  184.     }
  185.     close(INIT);
  186.  
  187.     # Check that all the required headers are present
  188.     if (!$lsbinfo{found}) {
  189.     printf STDERR "update-rc.d: warning: $initdscript missing LSB information\n";
  190.     printf STDERR "update-rc.d: see <http://wiki.debian.org/LSBInitScripts>\n";
  191.     } else {
  192.         for my $key (split(/\|/, lc($lsbheaders))) {
  193.             if (!exists $lsbinfo{$key}) {
  194.                 warning "$initdscript missing LSB keyword '$key'\n";
  195.             }
  196.         }
  197.     }
  198. }
  199.  
  200.  
  201. # Process the arguments after the "enable" or "disable" keyword.
  202.  
  203. sub toggle {
  204.     my @argv = @_;
  205.     my ($action, %lvls, @start, @stop, @xstartlinks);
  206.  
  207.     if (!&checklinks) {
  208.     print " System start/stop links for $initd/$bn do not exist.\n";
  209.     exit (0);
  210.     }
  211.  
  212.     $action = $argv[0];
  213.     if ($#argv > 1) {
  214.     while ($#argv > 0 && shift @argv) {
  215.         if ($argv[0] =~ /^[S2-5]$/) {
  216.         $lvls{$argv[0]}++;
  217.         } else {
  218.         &usage ("expected 'S' '2' '3' '4' or '5'");
  219.         }
  220.     }
  221.     } else {
  222.     $lvls{$_}++ for ('S', '2', '3', '4', '5');
  223.     }
  224.  
  225.     push(@start, glob($etcd . '[2-5S].d/[KS][0-9][0-9]' . $bn));
  226.  
  227.     foreach (@start) {
  228.     my $islink = &is_link (undef, $_, $bn);
  229.     next if !$islink;
  230.  
  231.     next unless my ($lvl, $sk, $seq) = m/^$etcd([2-5S])\.d\/([SK])([0-9]{2})$bn$/;
  232.     $startlinks[$lvl] = $sk . $seq;
  233.  
  234.     if ($action eq 'disable' and $sk eq 'S' and $lvls{$lvl}) {
  235.         $xstartlinks[$lvl] = 'K' . sprintf "%02d", (100 - $seq);
  236.     } elsif ($action eq 'enable' and $sk eq 'K' and $lvls{$lvl}) {
  237.         $xstartlinks[$lvl] = 'S' . sprintf "%02d", -($seq - 100);
  238.     } else {
  239.         $xstartlinks[$lvl] = $sk . $seq;
  240.     }
  241.     }
  242.  
  243.     push(@stop, glob($etcd . '[016].d/[KS][0-9][0-9]' . $bn));
  244.  
  245.     foreach (@stop) {
  246.     my $islink = &is_link (undef, $_, $bn);
  247.     next if !$islink;
  248.  
  249.     next unless my ($lvl, $sk, $seq) = m/^$etcd([016])\.d\/([SK])([0-9]{2})$bn$/;
  250.     $stoplinks[$lvl] = $sk . $seq;
  251.     }
  252.  
  253.     if ($action eq 'disable') {
  254.     print " Disabling system startup links for $initd/$bn ...\n";
  255.     } elsif ($action eq 'enable') {
  256.     print " Enabling system startup links for $initd/$bn ...\n";
  257.     }
  258.  
  259.     &checklinks ("remove");
  260.     @startlinks = @xstartlinks;
  261.  
  262.     1;
  263. }
  264.  
  265. # Process the arguments after the "defaults" keyword.
  266.  
  267. sub defaults {
  268.     my @argv = @_;
  269.     my ($start, $stop) = (20, 20);
  270.  
  271.     &usage ("defaults takes only one or two codenumbers") if ($#argv > 2);
  272.     $start = $stop = $argv[1] if ($#argv >= 1);
  273.     $stop  =         $argv[2] if ($#argv >= 2);
  274.     &usage ("codenumber must be a number between 0 and 99")
  275.     if ($start !~ /^\d\d?$/ || $stop  !~ /^\d\d?$/);
  276.  
  277.     $start = sprintf("%02d", $start);
  278.     $stop  = sprintf("%02d", $stop);
  279.  
  280.     $stoplinks[$_]  = "K$stop"  for (0, 1, 6);
  281.     $startlinks[$_] = "S$start" for (2, 3, 4, 5);
  282.  
  283.     1;
  284. }
  285.  
  286. # Process the arguments after the start or stop keyword.
  287.  
  288. sub startstop {
  289.     my @argv = @_;
  290.     my($letter, $NN, $level);
  291.  
  292.     while ($#argv >= 0) {
  293.     if    ($argv[0] eq 'start') { $letter = 'S'; }
  294.     elsif ($argv[0] eq 'stop')  { $letter = 'K'; }
  295.     else {
  296.         &usage("expected start|stop");
  297.     }
  298.  
  299.     if ($argv[1] !~ /^\d\d?$/) {
  300.         &usage("expected NN after $argv[0]");
  301.     }
  302.     $NN = sprintf("%02d", $argv[1]);
  303.  
  304.     if ($argv[-1] ne '.') {
  305.         &usage("start|stop arguments not terminated by \".\"");
  306.     }
  307.  
  308.     shift @argv; shift @argv;
  309.     $level = shift @argv;
  310.     do {
  311.         if ($level !~ m/^[0-9S]$/) {
  312.         &usage(
  313.                "expected runlevel [0-9S] (did you forget \".\" ?)");
  314.         }
  315.         if (! -d "$etcd$level.d") {
  316.         print STDERR
  317.             "update-rc.d: $etcd$level.d: no such directory\n";
  318.         exit(1);
  319.         }
  320.         $level = 99 if ($level eq 'S');
  321.         $startlinks[$level] = "$letter$NN" if ($letter eq 'S');
  322.         $stoplinks[$level]  = "$letter$NN" if ($letter eq 'K');
  323.     } while (($level = shift @argv) ne '.');
  324.     }
  325.     1;
  326. }
  327.  
  328. # Create the links.
  329.  
  330. sub makelinks {
  331.     my($t, $i);
  332.     my @links;
  333.  
  334.     if (&checklinks) {
  335.     print " System start/stop links for $initd/$bn already exist.\n";
  336.     return 0;
  337.     }
  338.     print " Adding system startup for $initd/$bn ...\n";
  339.  
  340.     # nice unreadable perl mess :)
  341.  
  342.     for($t = 0; $t < 2; $t++) {
  343.     @links = $t ? @startlinks : @stoplinks;
  344.     for($i = 0; $i <= $#links; $i++) {
  345.         my $lvl = $i;
  346.         $lvl = 'S' if ($i == 99);
  347.         next if (!defined $links[$i] or $links[$i] eq '');
  348.         print "   $etcd$lvl.d/$links[$i]$bn -> ../init.d/$bn\n";
  349.         next if ($notreally);
  350.         symlink("../init.d/$bn", "$etcd$lvl.d/$links[$i]$bn")
  351.         || die("update-rc.d: symlink: $!\n");
  352.     }
  353.     }
  354.  
  355.     1;
  356. }
  357.  
  358. ## Dependency based
  359. sub insserv_updatercd {
  360.     my @args = @_;
  361.     my @opts;
  362.     my $scriptname;
  363.     my $action;
  364.     my $notreally = 0;
  365.  
  366.     my @orig_argv = @args;
  367.  
  368.     while($#args >= 0 && ($_ = $args[0]) =~ /^-/) {
  369.         shift @args;
  370.         if (/^-n$/) { push(@opts, $_); $notreally++; next }
  371.         if (/^-f$/) { push(@opts, $_); next }
  372.         if (/^-h|--help$/) { &usage; }
  373.         usage("unknown option");
  374.     }
  375.  
  376.     usage("not enough arguments") if ($#args < 1);
  377.  
  378.     $scriptname = shift @args;
  379.     $action = shift @args;
  380.     if ("remove" eq $action) {
  381.         if ( -f "/etc/init.d/$scriptname" ) {
  382.             my $rc = system "insserv", @opts, "-r", $scriptname;
  383.             if (0 == $rc && !$notreally) {
  384.                 save_last_action($scriptname, @orig_argv);
  385.             }
  386.             exit $rc;
  387.         } else {
  388.             # insserv removes all dangling symlinks, no need to tell it
  389.             # what to look for.
  390.             my $rc = system "insserv", @opts;
  391.             if (0 == $rc && !$notreally) {
  392.                 save_last_action($scriptname, @orig_argv);
  393.             }
  394.             exit $rc;
  395.         }
  396.     } elsif ("defaults" eq $action || "start" eq $action ||
  397.              "stop" eq $action) {
  398.         # All start/stop/defaults arguments are discarded so emit a
  399.         # message if arguments have been given and are in conflict
  400.         # with Default-Start/Default-Stop values of LSB comment.
  401.         cmp_args_with_defaults($scriptname, $action, @args);
  402.  
  403.         if ( -f "/etc/init.d/$scriptname" ) {
  404.             my $rc = system "insserv", @opts, $scriptname;
  405.             if (0 == $rc && !$notreally) {
  406.                 save_last_action($scriptname, @orig_argv);
  407.             }
  408.             exit $rc;
  409.         } else {
  410.             error("initscript does not exist: /etc/init.d/$scriptname");
  411.         }
  412.     } elsif ("disable" eq $action || "enable" eq $action) {
  413.         insserv_toggle($notreally, $action, $scriptname, @args);
  414.         # Call insserv to resequence modified links
  415.         my $rc = system "insserv", @opts, $scriptname;
  416.         if (0 == $rc && !$notreally) {
  417.             save_last_action($scriptname, @orig_argv);
  418.         }
  419.         exit $rc;
  420.     } else {
  421.         usage();
  422.     }
  423. }
  424.  
  425. sub parse_def_start_stop {
  426.     my $script = shift;
  427.     my (%lsb, @def_start_lvls, @def_stop_lvls);
  428.  
  429.     open my $fh, '<', $script or error("unable to read $script");
  430.     while (<$fh>) {
  431.         chomp;
  432.         if (m/^### BEGIN INIT INFO$/) {
  433.             $lsb{'begin'}++;
  434.         }
  435.         elsif (m/^### END INIT INFO$/) {
  436.             $lsb{'end'}++;
  437.             last;
  438.         }
  439.         elsif ($lsb{'begin'} and not $lsb{'end'}) {
  440.             if (m/^# Default-Start:\s*(\S?.*)$/) {
  441.                 @def_start_lvls = split(' ', $1);
  442.             }
  443.             if (m/^# Default-Stop:\s*(\S?.*)$/) {
  444.                 @def_stop_lvls = split(' ', $1);
  445.             }
  446.         }
  447.     }
  448.     close($fh);
  449.  
  450.     return (\@def_start_lvls, \@def_stop_lvls);
  451. }
  452.  
  453. sub lsb_header_for_script {
  454.     my $name = shift;
  455.  
  456.     foreach my $file ("/etc/insserv/overrides/$name", "/etc/init.d/$name",
  457.                       "/usr/share/insserv/overrides/$name") {
  458.         return $file if -s $file;
  459.     }
  460.  
  461.     error("cannot find a LSB script for $name");
  462. }
  463.  
  464. sub cmp_args_with_defaults {
  465.     my ($name, $act) = (shift, shift);
  466.     my ($lsb_start_ref, $lsb_stop_ref, $arg_str, $lsb_str);
  467.     my (@arg_start_lvls, @arg_stop_lvls, @lsb_start_lvls, @lsb_stop_lvls);
  468.  
  469.     ($lsb_start_ref, $lsb_stop_ref) = parse_def_start_stop("/etc/init.d/$name");
  470.     @lsb_start_lvls = @$lsb_start_ref;
  471.     @lsb_stop_lvls  = @$lsb_stop_ref;
  472.     return if (!@lsb_start_lvls and !@lsb_stop_lvls);
  473.  
  474.     if ($act eq 'defaults') {
  475.         @arg_start_lvls = (2, 3, 4, 5);
  476.         @arg_stop_lvls  = (0, 1, 6);
  477.     } elsif ($act eq 'start' or $act eq 'stop') {
  478.         my $start = $act eq 'start' ? 1 : 0;
  479.         my $stop = $act eq 'stop' ? 1 : 0;
  480.  
  481.         # The legacy part of this program passes arguments starting with
  482.         # "start|stop NN x y z ." but the insserv part gives argument list
  483.         # starting with sequence number (ie. strips off leading "start|stop")
  484.         # Start processing arguments immediately after the first seq number.
  485.         my $argi = $_[0] eq $act ? 2 : 1;
  486.  
  487.         while (defined $_[$argi]) {
  488.             my $arg = $_[$argi];
  489.  
  490.             # Runlevels 0 and 6 are always stop runlevels
  491.             if ($arg eq 0 or $arg eq 6) {
  492.         $start = 0; $stop = 1; 
  493.             } elsif ($arg eq 'start') {
  494.                 $start = 1; $stop = 0; $argi++; next;
  495.             } elsif ($arg eq 'stop') {
  496.                 $start = 0; $stop = 1; $argi++; next;
  497.             } elsif ($arg eq '.') {
  498.                 next;
  499.             }
  500.             push(@arg_start_lvls, $arg) if $start;
  501.             push(@arg_stop_lvls, $arg) if $stop;
  502.         } continue {
  503.             $argi++;
  504.         }
  505.     }
  506.  
  507.     if ($#arg_start_lvls != $#lsb_start_lvls or
  508.         join("\0", sort @arg_start_lvls) ne join("\0", sort @lsb_start_lvls)) {
  509.         $arg_str = @arg_start_lvls ? "@arg_start_lvls" : "none";
  510.         $lsb_str = @lsb_start_lvls ? "@lsb_start_lvls" : "none";
  511.         warning "$name start runlevel arguments ($arg_str) do not match",
  512.                 "LSB Default-Start values ($lsb_str)";
  513.     }
  514.     if ($#arg_stop_lvls != $#lsb_stop_lvls or
  515.         join("\0", sort @arg_stop_lvls) ne join("\0", sort @lsb_stop_lvls)) {
  516.         $arg_str = @arg_stop_lvls ? "@arg_stop_lvls" : "none";
  517.         $lsb_str = @lsb_stop_lvls ? "@lsb_stop_lvls" : "none";
  518.         warning "$name stop runlevel arguments ($arg_str) do not match",
  519.                 "LSB Default-Stop values ($lsb_str)";
  520.     }
  521. }
  522.  
  523. sub insserv_toggle {
  524.     my ($dryrun, $act, $name) = (shift, shift, shift);
  525.     my (@toggle_lvls, $start_lvls, $stop_lvls, @symlinks);
  526.     my $lsb_header = lsb_header_for_script($name);
  527.  
  528.     # Extra arguments to disable|enable action are runlevels. If none
  529.     # given parse LSB info for Default-Start value.
  530.     if ($#_ >= 0) {
  531.         @toggle_lvls = @_;
  532.     } else {
  533.         ($start_lvls, $stop_lvls) = parse_def_start_stop($lsb_header);
  534.         @toggle_lvls = @$start_lvls;
  535.         if ($#toggle_lvls < 0) {
  536.             error("$name Default-Start contains no runlevels, aborting.");
  537.         }
  538.     }
  539.  
  540.     # Find symlinks in rc.d directories. Refuse to modify links in runlevels
  541.     # not used for normal system start sequence.
  542.     for my $lvl (@toggle_lvls) {
  543.         if ($lvl !~ /^[S2345]$/) {
  544.             warning("$act action will have no effect on runlevel $lvl");
  545.             next;
  546.         }
  547.         push(@symlinks, $_) for glob("/etc/rc$lvl.d/[SK][0-9][0-9]$name");
  548.     }
  549.  
  550.     if (!@symlinks) {
  551.         error("no runlevel symlinks to modify, aborting!");
  552.     }
  553.  
  554.     # Toggle S/K bit of script symlink.
  555.     for my $cur_lnk (@symlinks) {
  556.         my $sk;
  557.         my @new_lnk = split(//, $cur_lnk);
  558.  
  559.         if ("disable" eq $act) {
  560.             $sk = rindex($cur_lnk, '/S') + 1;
  561.             next if $sk < 1;
  562.             $new_lnk[$sk] = 'K';
  563.         } else {
  564.             $sk = rindex($cur_lnk, '/K') + 1;
  565.             next if $sk < 1;
  566.             $new_lnk[$sk] = 'S';
  567.         }
  568.  
  569.         if ($dryrun) {
  570.             printf("rename(%s, %s)\n", $cur_lnk, join('', @new_lnk));
  571.             next;
  572.         }
  573.  
  574.         rename($cur_lnk, join('', @new_lnk)) or error($!);
  575.     }
  576. }
  577.